<?php

	defined('MF_DB_PORT') or define('MF_DB_PORT',3306);
	defined('MF_DB_SSL_CA') or define('MF_DB_SSL_CA','');

/*
session table:

CREATE TABLE `session_handler_table` (
`id` varchar(255) NOT NULL,
`data` mediumtext NOT NULL,
`timestamp` int(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/

class DatabaseSessionHandler implements SessionHandlerInterface
{
    /**
     * a database PDO MySQL connection resource
     * @var object
     */
    protected $dbConnection;
    
    /**
     * the name of the DB table which handles the sessions
     * @var string
     */
    protected $dbTable;

    public function setDbDetails()
    {
        try {
            $dsn = 'mysql:host='.MF_DB_HOST.';port='.MF_DB_PORT.';dbname='.MF_DB_NAME;
            
            $options = [];
            $options[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;

            //use SSL connection if SSL certificate provided
            if (!empty('MF_DB_SSL_CA') && file_exists(MF_DB_SSL_CA)) {
                $options[PDO::MYSQL_ATTR_SSL_CA] = MF_DB_SSL_CA;
                $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = true;
            }

            $this->dbConnection = new PDO($dsn, MF_DB_USER, MF_DB_PASSWORD,$options);
            
            $this->dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->dbConnection->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
            
            $this->dbConnection->query("SET NAMES utf8mb4");
            $this->dbConnection->query("SET sql_mode = ''");
        } catch(PDOException $e) {
            die("Error connecting to the database: ".$e->getMessage());
        }
    }

    public function setDbConnection($dbConnection)
    {
        $this->dbConnection = $dbConnection;
    }

    public function setDbTable($dbTable)
    {
        $this->dbTable = $dbTable;
    }

    /**
     * Open the session
     * @param string $savePath
     * @param string $sessionName
     * @return bool
     */
    public function open($savePath, $sessionName): bool
    {
        //delete old session handlers
        $limit = time() - (3600 * 24);

        $query = "DELETE FROM " . $this->dbTable . " WHERE timestamp < " . $limit;
        $sth = $this->do_query($query, [], $this->dbConnection);

        return ($sth !== false);
    }

    /**
     * Close the session
     * @return bool
     */
    public function close(): bool
    {
        return true;
    }

    /**
     * Read the session
     * @param string $id session id
     * @return string string of the session or empty string on failure
     */
    public function read($id): string
    {
        $query = "SELECT data FROM " . $this->dbTable . " WHERE id = ?";
        $sth = $this->do_query($query, [$id], $this->dbConnection);
        $row = $this->do_fetch_result($sth);
        
        return $row['data'] ?? '';
    }

    /**
     * Write the session
     * @param string $id session id
     * @param string $data data of the session
     * @return bool
     */
    public function write($id, $data): bool
    {
        $query = "INSERT INTO `". $this->dbTable ."` VALUES(?,?,?) ON DUPLICATE KEY UPDATE `data`=?,`timestamp`=?;";
        
        $time = time();
        $params = array($id, $data, $time, $data, $time);
        $sth = $this->do_query($query, $params, $this->dbConnection);

        return ($sth !== false);
    }

    /**
     * Destroy the session
     * @param string $id session id
     * @return bool
     */
    public function destroy($id): bool
    {
        $query = "DELETE FROM `" . $this->dbTable . "` WHERE `id` = ?";
        $sth = $this->do_query($query, [$id], $this->dbConnection);

        return ($sth !== false);
    }

    /**
     * Garbage Collector
     * @param int $maxlifetime life time (sec.)
     * @return int|false Returns the number of deleted sessions on success, or false on failure
     */
    #[\ReturnTypeWillChange]
    public function gc($maxlifetime) // removed return type
    {
        $query = "DELETE FROM `{$this->dbTable}` WHERE `timestamp` < '" . (time() - intval($maxlifetime)) . "'";
        $sth = $this->do_query($query, [], $this->dbConnection);

        if ($sth === false) {
            return false;
        }
        
        // Return the number of deleted rows
        return $sth->rowCount();
    }

    private function do_query($query, $params, $dbh)
    {
        $sth = $dbh->prepare($query);
        try {
            $sth->execute($params);
        } catch(PDOException $e) {
            error_log("MySQL Error. Query Failed: ".$e->getMessage());
            
            $sth->debugDumpParams();
            echo("Query Failed: ".$e->getMessage());
            return false;            
        }

        return $sth;
    }

    private function do_fetch_result($sth)
    {
        return $sth->fetch(PDO::FETCH_ASSOC);    
    }
}


?>